package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.heima.article.service.HotArticleService;
import com.heima.common.exception.CustException;
import com.heima.constants.message.HotArticleConstants;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.model.mess.app.NewBehaviorDTO;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;

@Component
@Slf4j
public class UpdateHotArticleJob {
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    HotArticleService hotArticleService;

    //定时更新热点文章
    @XxlJob("updateHotArticleJob")
    public ReturnT updateHotArticleHandler(String params) {
        log.info("热文章分值更新 调度任务开始执行....");
        //查询redis行为列表的数据
        List<NewBehaviorDTO> behaviorList = getRedisBehaviorList();
        //非空判断
        if (behaviorList == null || behaviorList.size() == 0) {
            CustException.cust(AppHttpCodeEnum.AP_USER_DATA_NOT_EXIST);
        }
        //将行为数据按照文章分组并计算分值
        List<AggBehaviorDTO> aggBehaviorList = getAggBehaviorList(behaviorList);
        //调用hotArticleService方法重新计算文章热度,更新redis缓存
        aggBehaviorList.forEach(hotArticleService::updateApArticle);
        log.info("热文章分值更新 调度任务完成....");
        return ReturnT.SUCCESS;
    }

    //将行为数据按照文章分组并计算分值
    public List<AggBehaviorDTO> getAggBehaviorList(List<NewBehaviorDTO> behaviorList) {
        //使用stream流按照articleId分组
        Map<Long, List<NewBehaviorDTO>> map = behaviorList.stream().collect(Collectors.groupingBy(NewBehaviorDTO::getArticleId));
        //创建List<AggBehaviorDTO>集合  保存map集合遍历后转换的行为聚合对象
        List<AggBehaviorDTO> aggBehaviorList = new ArrayList<>();
        //遍历map集合
        map.forEach((articleId, newBehaviorList) -> {
            Optional<AggBehaviorDTO> result = newBehaviorList.stream().map(newBehavior -> {
                //创建AggBehaviorDTO行为聚合对象
                AggBehaviorDTO aggBehaviorDTO = new AggBehaviorDTO();
                aggBehaviorDTO.setArticleId(newBehavior.getArticleId());
                //对newBehavior进行行为类型判断
                switch (newBehavior.getType()) {
                    case LIKES:
                        aggBehaviorDTO.setLike(newBehavior.getAdd());
                        break;
                    case COMMENT:
                        aggBehaviorDTO.setComment(newBehavior.getAdd());
                        break;
                    case COLLECTION:
                        aggBehaviorDTO.setCollect(newBehavior.getAdd());
                        break;
                    case VIEWS:
                        aggBehaviorDTO.setView(newBehavior.getAdd());
                        break;
                }
                //遍历一个文章的所有行为数据后,返回的是aggBehaviorDTO组成的集合的stream流
                return aggBehaviorDTO;
                //对返回的aggBehaviorDTO组成的集合的stream流进行归并操作
            }).reduce((a1, a2) -> {
                a1.setLike(a1.getLike() + a2.getLike());
                a1.setCollect(a1.getCollect() + a2.getCollect());
                a1.setComment(a1.getComment() + a2.getComment());
                a1.setLike(a1.getLike() + a2.getLike());
                return a1;
            });
            //判断是否存在结果
            if (result.isPresent()) {
                //获取结果
                AggBehaviorDTO aggBehaviorDTO = result.get();
                //添加到创建的集合
                aggBehaviorList.add(aggBehaviorDTO);
            }
        });
        return aggBehaviorList;
    }

    //获取redis list列表中的待处理行为数据
//    private List<NewBehaviorDTO> getRedisBehaviorList() {
//        //获取redis中待处理行为列表
//        ListOperations resultList = redisTemplate.opsForList();
//        //获取待处理列表的size
//        Long size = resultList.size(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST);
//        //判断
//        if (size == 0) {
//            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
//        }
//        //多组命令使用管道,保证原子性
//        List result = redisTemplate.executePipelined(new RedisCallback<List<NewBehaviorDTO>>() {
//            @Override
//            public List<NewBehaviorDTO> doInRedis(RedisConnection connection) throws DataAccessException {
//                //开启管道执行命令
//                connection.openPipeline();
//                //开启redis事务队列
//                connection.multi();
//                //获取0到size-1的所有集合数据 不使用-1是因为可能在执行中队列插入了新的数据
//                connection.lRange(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), 0, (size - 1));
//                //保留size到-1的数据(我们执行上面查询的语句后,新增的数据就在size到-1),也就是删除上面查询到的0到size-1的数据
//                connection.lTrim(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), size, -1);
//                //执行事务
//                connection.exec();
//                return null;
//            }
//        }, RedisSerializer.string());
//        //非空判断
//        if (result.size() > 0) {
//            //获取集合中第一个元素
//            List<String> newBehaviorDTOS = (List<String>)result.get(0);
//            String s = newBehaviorDTOS.get(0);
//            List<NewBehaviorDTO> behaviorList = newBehaviorDTOS.stream().map(str -> {
//                return JSON.parseObject(str, NewBehaviorDTO.class);
//            }).collect(Collectors.toList());
//            //返回集合
//            return behaviorList;
//        }
//        //如果result.size()==0,返回null
//        return null;
//    }

    /**
     * 获取redis list列表中的待处理行为数据
     * @return
     */
    private List<NewBehaviorDTO> getRedisBehaviorList() {
        try {
            //调用lua脚本并执行
            DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
            // 设置脚本的返回结果
            redisScript.setResultType(List.class);
            //lua文件存放在resources目录下的redis文件夹内
            redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis.lua")));
            // 执行lua脚本
            List<String> result = redisTemplate.execute(redisScript, Arrays.asList(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST) );
            // 将查询到的result 解析为NewsBehaviorDTO行为对象  并存储到集合返回
            return result.stream()
                    .map(jsonStr -> JSON.parseObject(jsonStr,NewBehaviorDTO.class))
                    .collect(Collectors.toList());
        } catch (Exception e) {
            e.printStackTrace();
            CustException.cust(AppHttpCodeEnum.SERVER_ERROR,"执行lua脚本失败");
        }
        return null;
    }
}